package com.nsn.vs.sdm.component.svn.command;

import java.io.File;
import java.util.Iterator;

import org.tmatesoft.svn.core.ISVNLogEntryHandler;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.SVNLogClient;
import org.tmatesoft.svn.core.wc.SVNRevision;

import com.nsn.vs.sdm.component.svn.SvnClientManager;

/**
 * Used for executing svn fetch log. 
 * Output is similar to command line client except
 * that it doesn't (yet) include the number of lines that have changed in the
 * revision and the date format is different.
 * 
 * <pre>
 * Includes these optional params:
 * url: use a svn url rather than a path to a working copy
 * startRevision: revision number or "HEAD" or "BASE" (defaults to "HEAD")
 * endRevision: revision number or "HEAD" or "BASE" (defaults to "BASE")
 * limit: max number of log entries to get, 0 for all (defaults to 0)
 * stopOnCopy: do we stop on copy? (defaults to false)
 * discoverChangedPaths: do we report of all changed paths for every revision being processed? (defaults to false)
 * </pre>
 * 
 * @author Jeccy.Zhao
 * 
 */
public class SvnCommandFetchLog extends SvnCommand 
{
	/**
	 * The system line separator
	 */
	private static final String LINE_SEPARATOR = System.getProperty("line.separator", "\n");

	/**
	 * The log item separator
	 */
	private static final String ITEM_SEPARATOR = "----------------------------------------------------------------------------";

	/**
	 * The repository address
	 */
	private String url;

	/**
	 * The local path
	 */
	private String path;

	/**
	 * The start revision number (defaults to "HEAD")
	 */
	private String startRevision;

	/**
	 * The end revision number (defaults to "BASIC")
	 */
	private String endRevision;
	
	/**
	 * The max number of log entries to get, 0 for all (defaults to 0)
	 */
	private long limit;
	
	/**
	 * do we stop on copy? (defaults to false)
	 */
	private boolean stopOnCopy;
	
	/**
	 * report of all changed paths for every revision being processed? (defaults to false)
	 */
	private boolean discoverChangedPaths;
	
	private boolean includeMergedRevisions;
	
	/**
	 * 
	 * @param svnClient
	 */
	public SvnCommandFetchLog(SvnClientManager svnClient) 
	{
		super(svnClient);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nsn.hzta.tcr.component.svn.SvnCommand#execute()
	 */
	@Override
	@SuppressWarnings("deprecation")
	public long execute() throws Exception 
	{
		SVNLogClient client = this.svnClient.getManager().getLogClient();

		SVNRevision start = SVNRevision.parse(this.startRevision);
		SVNRevision end = SVNRevision.parse(this.endRevision);

		final StringBuilder logBuffer = new StringBuilder(1024);

		if (this.url == null) 
		{
			File filePath = new File(this.path);
			client.doLog(new File[] { filePath }, end, start, end,
					this.stopOnCopy, this.discoverChangedPaths,
					this.includeMergedRevisions, this.limit, new String[] {},
					this.getLogEntryHandler(logBuffer));
		}
		else
		{
			String path = (this.path == null || "".equals(this.path)) ? "/" : this.path;
			client.doLog(SVNURL.parseURIDecoded(this.url), new String[] { path }, end, start, end, this.stopOnCopy, this.discoverChangedPaths, this.includeMergedRevisions, this.limit, new String[] { }, this.getLogEntryHandler(logBuffer));
		}
		
		logBuffer.append(ITEM_SEPARATOR + LINE_SEPARATOR);
		
		System.out.println (logBuffer.toString());
		
		return 0;
	}
	
	/**
	 * 
	 * @param logBuffer
	 * @return
	 */
	private ISVNLogEntryHandler getLogEntryHandler(final StringBuilder logBuffer) 
	{
		return new ISVNLogEntryHandler() 
		{
			public void handleLogEntry(SVNLogEntry entry) throws SVNException 
			{
				logBuffer.append(ITEM_SEPARATOR).append(LINE_SEPARATOR)
						.append('r').append(entry.getRevision()).append(" | ")
						.append(entry.getAuthor()).append(" | ")
						.append(entry.getDate()).append(LINE_SEPARATOR)
						.append(entry.getMessage()).append(LINE_SEPARATOR);

				if (SvnCommandFetchLog.this.discoverChangedPaths) 
				{
					logBuffer.append("Changed paths:").append(LINE_SEPARATOR);
					for (Iterator<?> paths = entry.getChangedPaths().values().iterator(); paths.hasNext();) 
					{
						SVNLogEntryPath path = (SVNLogEntryPath) paths.next();
						logBuffer.append(path.toString()).append(LINE_SEPARATOR);
					}
				}
			}
		};
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nsn.hzta.tcr.component.svn.SvnCommand#validateAttributes()
	 */
	@Override
	protected void validateAttributes() throws Exception 
	{
		if (this.url == null && this.path == null) 
		{
			throw new NullPointerException("must specify path or url");
		}

		if (this.startRevision == null) 
		{
			this.startRevision = "HEAD";
		}

		if (this.endRevision == null) 
		{
			this.endRevision = "BASE";
		}
		
		if (this.limit < 0)
		{
			this.limit = 0;
		}

	}

	public static String getLineSeparator() 
	{
		return LINE_SEPARATOR;
	}

	public static String getItemSeparator() 
	{
		return ITEM_SEPARATOR;
	}

	public String getUrl() 
	{
		return url;
	}

	public void setUrl(String url) 
	{
		this.url = url;
	}

	public String getPath() 
	{
		return path;
	}

	public void setPath(String path) 
	{
		this.path = path;
	}

	public String getStartRevision() 
	{
		return startRevision;
	}

	public void setStartRevision(String startRevision) 
	{
		this.startRevision = startRevision;
	}

	public String getEndRevision() 
	{
		return endRevision;
	}

	public void setEndRevision(String endRevision) 
	{
		this.endRevision = endRevision;
	}

	public long getLimit() 
	{
		return limit;
	}

	public void setLimit(long limit) 
	{
		this.limit = limit;
	}

	public boolean isStopOnCopy() 
	{
		return stopOnCopy;
	}

	public void setStopOnCopy(boolean stopOnCopy) 
	{
		this.stopOnCopy = stopOnCopy;
	}

	public boolean isDiscoverChangedPaths() 
	{
		return discoverChangedPaths;
	}

	public void setDiscoverChangedPaths(boolean discoverChangedPaths) 
	{
		this.discoverChangedPaths = discoverChangedPaths;
	}

	public boolean isIncludeMergedRevisions() 
	{
		return includeMergedRevisions;
	}

	public void setIncludeMergedRevisions(boolean includeMergedRevisions) 
	{
		this.includeMergedRevisions = includeMergedRevisions;
	}

}
